﻿using Microsoft.Extensions.Logging;
using Serilog.Extensions.Logging;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Core.Info;
using Core.Log.SerilogExtend.Enricher;
using Core.Log.SerilogExtend;
using Microsoft.Extensions.Configuration;
using Serilog.Events;
using Serilog.Sinks.File.Archive;
using System.IO.Compression;
using Serilog.Core;
using Microsoft.VisualBasic;
using static System.Runtime.InteropServices.JavaScript.JSType;
using System.Diagnostics;

namespace Core.Log
{
    /// <summary>
    /// <para>MS.Trace * 1. Verbose	详细- 跟踪信息和调试细节；一般只在异常情况下开启;  Verbose 是最详细的级别，很少（如果有的话）为生产应用程序启用。</para>
    /// <para>MS.Debug * 2. Debug 调试- 内部控制流和诊断状态转储，以帮助查明已识别的问题;  调试用于内部系统事件，这些事件不一定可以从外部观察到，但在确定某事如何发生时很有用。</para>
    /// <para>MS.Information* 3. Information 信息- 感兴趣的事件或与外部观察者相关的事件；默认启用的最低日志记录级别;  信息事件描述了系统中发生的与其职责和功能相对应的事情。通常，这些是系统可以执行的可观察操作。</para>
    /// <para>MS.Warning* 4. Warning 警告- 可能出现问题或服务/功能降级的指标;  当服务降级、受到威胁或行为可能超出其预期参数时，将使用警告级别事件。</para>
    /// <para>MS.Error* 5. Error 错误- 表明应用程序或连接的系统中出现故障;  当功能不可用或预期被破坏时，将使用 Error 事件。</para>
    /// <para>MS.Critical* 6. Fatal 致命- 导致应用程序完全失败的严重错误;  最关键的级别，致命事件需要立即关注。</para>
    /// <para>* 默认级别- 如果未MinimumLevel指定，则将处理Information级别事件和更高级别的事件。</para>
    /// </summary>
    public static class SerilogLogger
    {
        /// <summary>
        /// 创建日志记录器
        /// </summary>
        /// <returns></returns>
        public static Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName = "")
        {
            var serilogLogger = LoggerConfiguration.CreateLogger();
            var microsoftLogger = new SerilogLoggerFactory(serilogLogger)
                .CreateLogger(categoryName); // creates an instance of ILogger
            return microsoftLogger;
        }

        /// <summary>
        /// 创建日志记录器
        /// </summary>
        /// <returns></returns>
        public static Microsoft.Extensions.Logging.ILogger CreateLogger<T>()
        {
            var serilogLogger = LoggerConfiguration.CreateLogger();
            var microsoftLogger = new SerilogLoggerFactory(serilogLogger)
                .CreateLogger<T>(); // creates an instance of ILogger
            return microsoftLogger;
        }

        /// <summary>
        /// 创建日志记录器
        /// </summary>
        /// <returns></returns>
        public static Microsoft.Extensions.Logging.ILogger CreateLogger(Type type)
        {
            var serilogLogger = LoggerConfiguration.CreateLogger();
            var microsoftLogger = new SerilogLoggerFactory(serilogLogger)
                .CreateLogger(type); // creates an instance of ILogger
            return microsoftLogger;
        }

        /// <summary>
        /// 日志输出模板
        /// </summary>
        public static string OutputTemplate
        {
            get
            {
                //模板参考文档：https://github.com/serilog/serilog/wiki/Configuration-Basics
                //模板格式化文档：https://github.com/serilog/serilog/wiki/Formatting-Output
                var outputTemplate =
                    "{NewLine}[{Level:u4}] {Timestamp:yyyy-MM-dd HH:mm:ss.ffff} [线程Id： {ThreadId}]" +
                    "{NewLine}应用信息： {AppInfo:j}" +
                    "{NewLine}" +
                    new string('-', 15) + " 日志信息开始 " + new string('-', 15) +
                    "{NewLine}" +
                    "{Message:lj}" +
                    "{NewLine}" +
                    new string('-', 15) + " 日志信息结束 " + new string('-', 15) +
                    "{NewLine}日志属性： {Properties:j}" +
                    "{NewLine}{Exception}" +
                    "{NewLine}" +
                    new string('=', 50) +
                    "{NewLine}" +
                    "{NewLine}";//输出模板
                return outputTemplate;
            }
        }

        /// <summary>
        /// 获取默认配置
        /// </summary>
        public static LoggerConfiguration LoggerConfiguration
        {
            get
            {
                LoggingLevelSwitch levelSwitch = new LoggingLevelSwitch(LogEventLevel.Information);
                LoggerConfiguration? _loggerConfiguration = null;
                //模板参考文档：https://github.com/serilog/serilog/wiki/Configuration-Basics
                //模板格式化文档：https://github.com/serilog/serilog/wiki/Formatting-Output
                var _outputTemplate =
                    "{NewLine}[{Level:u4}] {Timestamp:yyyy-MM-dd HH:mm:ss.ffff} [线程Id： {ThreadId}]" +
                    "{NewLine}应用信息： {AppInfo:j}" +
                    "{NewLine}" +
                    new string('-', 15) + " 日志信息开始 " + new string('-', 15) +
                    "{NewLine}" +
                    "{Message:lj}" +
                    "{NewLine}" +
                    new string('-', 15) + " 日志信息结束 " + new string('-', 15) +
                    "{NewLine}日志属性： {Properties:j}" +
                    "{NewLine}{Exception}" +
                    "{NewLine}" +
                    new string('=', 50) +
                    "{NewLine}" +
                    "{NewLine}";//输出模板

                /* 日志级别说明
                   MS.Trace * 1. Verbose	详细- 跟踪信息和调试细节；一般只在异常情况下开启;  Verbose 是最详细的级别，很少（如果有的话）为生产应用程序启用。
                   MS.Debug * 2. Debug	调试- 内部控制流和诊断状态转储，以帮助查明已识别的问题;  调试用于内部系统事件，这些事件不一定可以从外部观察到，但在确定某事如何发生时很有用。
                   MS.Information * 3. Information	信息- 感兴趣的事件或与外部观察者相关的事件；默认启用的最低日志记录级别;  信息事件描述了系统中发生的与其职责和功能相对应的事情。通常，这些是系统可以执行的可观察操作。
                   MS.Warning * 4. Warning	警告- 可能出现问题或服务/功能降级的指标;  当服务降级、受到威胁或行为可能超出其预期参数时，将使用警告级别事件。
                   MS.Error * 5. Error	错误- 表明应用程序或连接的系统中出现故障;  当功能不可用或预期被破坏时，将使用 Error 事件。
                   MS.Critical * 6. Fatal	致命- 导致应用程序完全失败的严重错误;  最关键的级别，致命事件需要立即关注。
                    * 默认级别- 如果未MinimumLevel指定，则将处理Information级别事件和更高级别的事件。
                 */


                /* 日志可以组件参考
                 * https://github.com/serilog/serilog/wiki/Provided-Sinks
                 */

                //依赖包：Microsoft.Extensions.Configuration、Microsoft.Extensions.Configuration.FileExtensions、Microsoft.Extensions.Configuration.Json、Serilog.Settings.Configuration
                //参见：https://github.com/serilog/serilog-settings-configuration/

                //读取配置文件
                var settingsJsonConfig = AppLogFacotry.LogConfiguration;
                //var settingsConfig = new LoggerConfiguration().ReadFrom.Configuration(settingsJsonConfig);
                string? settingsConfigMinimumLevel = settingsJsonConfig?["Logging.LogLevel.Default"];
                string serilogMinimumLevel = string.IsNullOrEmpty(settingsConfigMinimumLevel) ? "Information" : settingsConfigMinimumLevel;

                levelSwitch.MinimumLevel = SerilogConverter.MicrosoftLogLevelStringToLogEventLevel(serilogMinimumLevel, levelSwitch.MinimumLevel);

                var logConfig = new LoggerConfiguration()
                    //配置Serilog.Events.LogEvents的扩展。Enricher可以添加、删除和修改与事件关联的属性。
                    //参考文档：https://github.com/serilog/serilog/wiki/Enrichment
                    .Enrich.With(new ThreadIdEnricher()) //扩展线程Id
                    .Enrich.With(new AppInfoEnricher()); //扩展应信息
                                                         //.MinimumLevel.Debug()
                                                         //.MinimumLevel.ControlledBy(_levelSwitch) //日志级别由_levelSwitch控制

                bool isEanbledConsole = settingsJsonConfig?["LoggerEnabledConsole"]?.ToLower() == "true" ? true : false;

                if (isEanbledConsole) {
                    logConfig = logConfig
                        .WriteTo.Console
                        (
                            outputTemplate: OutputTemplate,
                            applyThemeToRedirectedOutput: true
                        );
                }

                bool isEanbledDebugConsole = settingsJsonConfig?["LoggerEnabledDebugConsole"]?.ToLower() == "true" ? true : false;
                if (isEanbledDebugConsole) {
                    logConfig = logConfig.WriteTo.Debug
                    (
                        outputTemplate: OutputTemplate
                    );
                }
                //滚动策略：按天滚动，最多保存31个文件，不限制文件大小
                logConfig = logConfig.WriteTo.File(
                    $"{Path.Combine(AppInfo.LogDirectory, ".log")}",//{AppDomain.CurrentDomain.BaseDirectory}/Logs/.log
                    outputTemplate: OutputTemplate,
                    //restrictedToMinimumLevel: LogEventLevel.Verbose,
                    rollingInterval: RollingInterval.Day,//日志按日保存，这样会在文件名称后自动加上日期后缀
                    rollOnFileSizeLimit: true,          // 限制单个文件的最大长度
                    retainedFileCountLimit: 31,         // 最大保存文件数,等于null时永远保留文件。
                                                        //fileSizeLimitBytes: 10 * 1024 * 1024,      // 最大单个文件大小
                    fileSizeLimitBytes: null,      // 最大单个文件大小
                    encoding: Encoding.UTF8, // 文件字符编码
                    shared: true //多进程共享文件
                                 //hooks: new ArchiveHooks(CompressionLevel.Fastest)
                )
                //滚动策略：按天滚动，最多保存100个文件，不限制文件大小
                .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Debug) //单独输出 Debug 级别
                    .WriteTo.File(
                        $"{Path.Combine(AppInfo.LogDirectory, $"{nameof(LogEventLevel.Debug)}_.log")}",//{AppDomain.CurrentDomain.BaseDirectory}/Logs/.log
                        outputTemplate: OutputTemplate,
                        //restrictedToMinimumLevel: LogEventLevel.Verbose,
                        rollingInterval: RollingInterval.Day,//日志按日保存，这样会在文件名称后自动加上日期后缀
                        rollOnFileSizeLimit: true,          // 限制单个文件的最大长度
                        retainedFileCountLimit: 31,         // 最大保存文件数,等于null时永远保留文件。
                                                            //fileSizeLimitBytes: 10 * 1024 * 1024,      // 最大单个文件大小
                        fileSizeLimitBytes: null,      // 最大单个文件大小
                        encoding: Encoding.UTF8, // 文件字符编码
                        shared: false, //多进程共享文件
                        hooks: new ArchiveHooks(CompressionLevel.Fastest)
                ))
                //滚动策略：按天滚动，最多保存100个文件，不限制文件大小
                .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Error) //单独输出 Error 级别
                    .WriteTo.File(
                        $"{Path.Combine(AppInfo.LogDirectory, $"{nameof(LogEventLevel.Error)}_.log")}",//{AppDomain.CurrentDomain.BaseDirectory}/Logs/.log
                        outputTemplate: OutputTemplate,
                        //restrictedToMinimumLevel: LogEventLevel.Verbose,
                        rollingInterval: RollingInterval.Day,//日志按日保存，这样会在文件名称后自动加上日期后缀
                        rollOnFileSizeLimit: true,          // 限制单个文件的最大长度
                        retainedFileCountLimit: 31,         // 最大保存文件数,等于null时永远保留文件。
                                                            //fileSizeLimitBytes: 10 * 1024 * 1024,      // 最大单个文件大小
                        fileSizeLimitBytes: null,      // 最大单个文件大小
                        encoding: Encoding.UTF8, // 文件字符编码
                        shared: false, //多进程共享文件
                        hooks: new ArchiveHooks(CompressionLevel.Fastest)
                ))
                //滚动策略：按天滚动，最多保存100个文件，不限制文件大小
                .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Fatal) //单独输出 Fatal 级别
                    .WriteTo.File(
                        $"{Path.Combine(AppInfo.LogDirectory, $"{nameof(LogEventLevel.Fatal)}_.log")}",//{AppDomain.CurrentDomain.BaseDirectory}/Logs/.log
                        outputTemplate: OutputTemplate,
                        //restrictedToMinimumLevel: LogEventLevel.Verbose,
                        rollingInterval: RollingInterval.Day,//日志按日保存，这样会在文件名称后自动加上日期后缀
                        rollOnFileSizeLimit: true,          // 限制单个文件的最大长度
                        retainedFileCountLimit: 31,         // 最大保存文件数,等于null时永远保留文件。
                                                            //fileSizeLimitBytes: 10 * 1024 * 1024,      // 最大单个文件大小
                        fileSizeLimitBytes: null,      // 最大单个文件大小
                        encoding: Encoding.UTF8, // 文件字符编码
                        shared: false, //多进程共享文件
                        hooks: new ArchiveHooks(CompressionLevel.Fastest)
                ))
                //根据日志属性过滤
                .WriteTo.Logger(lg => lg.Filter.ByIncludingOnly(p =>
                {
                    LogEventPropertyValue result = p.Properties.FirstOrDefault(c => c.Key == "LogSource").Value;
                    if (result != null)
                    {
                        return result?.ToString() == "\"Custom\"";//指定的日志源
                    }
                    return false;
                })
                    .WriteTo.File(
                        $"{Path.Combine(AppInfo.LogDirectory, "Custom_.log")}",//{AppDomain.CurrentDomain.BaseDirectory}/Logs/.log
                        outputTemplate: OutputTemplate,
                        //restrictedToMinimumLevel: LogEventLevel.Verbose,
                        rollingInterval: RollingInterval.Day,//日志按日保存，这样会在文件名称后自动加上日期后缀
                        rollOnFileSizeLimit: true,          // 限制单个文件的最大长度
                        retainedFileCountLimit: 31,         // 最大保存文件数,等于null时永远保留文件。
                                                            //fileSizeLimitBytes: 10 * 1024 * 1024,      // 最大单个文件大小
                        fileSizeLimitBytes: null,      // 最大单个文件大小
                        encoding: Encoding.UTF8, // 文件字符编码
                        shared: false, //多进程共享文件                                    
                        hooks: new ArchiveHooks(CompressionLevel.Fastest)
                ));
                logConfig.MinimumLevel.ControlledBy(levelSwitch);
                _loggerConfiguration = logConfig;

                return _loggerConfiguration;
            }
        }
    }
}
